在上一篇中,我們討論到了 image 會把應用程式、相依的函式庫/套件,以及應用程式執行時所需的系統環境給打包起來,用這個 image 啟動的 container 就會有所有需要的資源,藉此我們達成了「Build once, Run anywhere」的夢想(?)。但這也引申了另外一個問題,這個 image 會不會很大呢?如果有兩個類似環境的應用程式,例如有兩個不同的 Node.js 程式要用 container 來執行,他們都需要 Node.js 16 這個版本的執行環境,那會不會包了兩個很大的 image 呢?
寫到這裡,應該很自然地會想到,是否可以把相同的部分抽取出來共用,我們能想到的,docker 當然也能想到,所以的確 docker 也這麼做了。我們先來查看一下目前 Host 裡有哪些 images (備註1):
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 2d389e545974 6 days ago 142MB
這時候讓我們用 docker image inspect nginx
(或是 docker image inspect 2d389e545974
)來查看一下這個 image 的細節,執行完這個指令後,一樣會看到一大串資訊,這次我們特別看一下 RootFS
這個區塊,跟 container 一樣,我們可以透過 format 跟 jq 來查看:
$ docker image inspect --format='{{json .RootFS}}' 2d389e545974 | jq
{
"Type": "layers",
"Layers": [
"sha256:b45078e74ec97c5e600f6d5de8ce6254094fb3cb4dc5e1cc8335fb31664af66e",
"sha256:9388548487b1997b925923c4711efa5c7fdf5a5203a36e8055ffe7960a6b9127",
"sha256:a064c1703bfdb529a4338259302cdf4d2407fddcedeabb9879b2332deb2df794",
"sha256:7ee9bf58503c7147275d30d50397bb67a643e58925764492ef3e2d8f375bccde",
"sha256:31192a8593ecba270815430c38c164c53ed65a765062b1b4b4aeca1ceec92d1e",
"sha256:36665e416ec8d9af3690bd5738f7642688252a2edf427e1a0ae6b1a1ca6748d0"
]
}
這邊可以看到他用的詞是layer
,然後這邊有 6 層 layers,這個 layer 是 docker 所設計的,也正是這個 layer 使我們之前的推測成真。除了 RootFS
這個區塊外,還有一個區塊 GraphDriver
特別值得注意:
$ docker image inspect --format='{{json .GraphDriver}}' 2d389e545974 | jq
{
"Data": {
"LowerDir": "/var/lib/docker/overlay2/b55fadceebc53ee1e133343d57bcb70b4a96659ebb0a4c83ffcf27a02fe495ee/diff:/var/lib/docker/overlay2/1d05141f1e9f2fbc9bdfb87a681976ce7db6f1dae3c12a6d94c00197e07da019/diff:/var/lib/docker/overlay2/c5dee5abf71f4c0ecb219c68f19ca56f64b0400739319ed482994fb58c297f79/diff:/var/lib/docker/overlay2/ed5b345b9d1a113c250527826f268a24e556fc67314340a6082b9627bf99fc36/diff:/var/lib/docker/overlay2/2607ebf4a87c9a99966ad99c7c096d541114a3b4f75ab25e2bac3bf614f3485b/diff",
"MergedDir": "/var/lib/docker/overlay2/8b0ad5ef739a7bb36a94fafce200ff939055225d5bcfd43b837ec3a5e8e595d6/merged",
"UpperDir": "/var/lib/docker/overlay2/8b0ad5ef739a7bb36a94fafce200ff939055225d5bcfd43b837ec3a5e8e595d6/diff",
"WorkDir": "/var/lib/docker/overlay2/8b0ad5ef739a7bb36a94fafce200ff939055225d5bcfd43b837ec3a5e8e595d6/work"
},
"Name": "overlay2"
}
這邊請特別注意 LowerDir
與 UpperDir
,他們是 Host 上的檔案夾路徑,讓我們整理一下 LowerDir
,可以看到它包含有五個檔案夾路徑:
/var/lib/docker/overlay2/b55fadceebc53ee1e133343d57bcb70b4a96659ebb0a4c83ffcf27a02fe495ee/diff
/var/lib/docker/overlay2/1d05141f1e9f2fbc9bdfb87a681976ce7db6f1dae3c12a6d94c00197e07da019/diff
/var/lib/docker/overlay2/c5dee5abf71f4c0ecb219c68f19ca56f64b0400739319ed482994fb58c297f79/diff
/var/lib/docker/overlay2/ed5b345b9d1a113c250527826f268a24e556fc67314340a6082b9627bf99fc36/diff
/var/lib/docker/overlay2/2607ebf4a87c9a99966ad99c7c096d541114a3b4f75ab25e2bac3bf614f3485b/diff
讓我們來做個小實驗:
nginx-a
,在這個 container 中建立一個檔案,並且在這檔案中加上一些內容$ docker run -it --name nginx-a nginx /bin/bash
root@e72d1a7dca58:/# touch a.txt
root@e72d1a7dca58:/# echo "a" > a.txt
mynginx:a
$ docker commit nginx-a mynginx:a
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
mynginx a c62870f5a42d 12 minutes ago 142MB
nginx latest 2d389e545974 7 days ago 142MB
nginx:a
,一樣特別觀察一下 RootFS
與 GraphDriver
這兩個區塊:$ docker image inspect --format='{{json .RootFS}}' mynginx:a | jq
{
"Type": "layers",
"Layers": [
"sha256:b45078e74ec97c5e600f6d5de8ce6254094fb3cb4dc5e1cc8335fb31664af66e",
"sha256:9388548487b1997b925923c4711efa5c7fdf5a5203a36e8055ffe7960a6b9127",
"sha256:a064c1703bfdb529a4338259302cdf4d2407fddcedeabb9879b2332deb2df794",
"sha256:7ee9bf58503c7147275d30d50397bb67a643e58925764492ef3e2d8f375bccde",
"sha256:31192a8593ecba270815430c38c164c53ed65a765062b1b4b4aeca1ceec92d1e",
"sha256:36665e416ec8d9af3690bd5738f7642688252a2edf427e1a0ae6b1a1ca6748d0",
"sha256:990cd1c1946b4594aafdb4a879edf16977013643c1a64e4ecd9bfe7fd3d4fb67"
]
}
跟 image nginx
比較一下,有沒有發現,這邊多了一層 layer,變成 7 層了,而且前面的 6 層 layers 跟 nginx
的一模一樣。
再讓我們看一下 GraphDriver
這個區塊:
docker image inspect --format='{{json .GraphDriver}}' mynginx:a | jq
{
"Data": {
"LowerDir": "/var/lib/docker/overlay2/8b0ad5ef739a7bb36a94fafce200ff939055225d5bcfd43b837ec3a5e8e595d6/diff:/var/lib/docker/overlay2/b55fadceebc53ee1e133343d57bcb70b4a96659ebb0a4c83ffcf27a02fe495ee/diff:/var/lib/docker/overlay2/1d05141f1e9f2fbc9bdfb87a681976ce7db6f1dae3c12a6d94c00197e07da019/diff:/var/lib/docker/overlay2/c5dee5abf71f4c0ecb219c68f19ca56f64b0400739319ed482994fb58c297f79/diff:/var/lib/docker/overlay2/ed5b345b9d1a113c250527826f268a24e556fc67314340a6082b9627bf99fc36/diff:/var/lib/docker/overlay2/2607ebf4a87c9a99966ad99c7c096d541114a3b4f75ab25e2bac3bf614f3485b/diff",
"MergedDir": "/var/lib/docker/overlay2/e6f2ed0e6d55115918467a74b4dfab3f3423c346cccdbe68763de8727061bf20/merged",
"UpperDir": "/var/lib/docker/overlay2/e6f2ed0e6d55115918467a74b4dfab3f3423c346cccdbe68763de8727061bf20/diff",
"WorkDir": "/var/lib/docker/overlay2/e6f2ed0e6d55115918467a74b4dfab3f3423c346cccdbe68763de8727061bf20/work"
},
"Name": "overlay2"
}
整理 LowerDir
:
/var/lib/docker/overlay2/8b0ad5ef739a7bb36a94fafce200ff939055225d5bcfd43b837ec3a5e8e595d6/diff
/var/lib/docker/overlay2/b55fadceebc53ee1e133343d57bcb70b4a96659ebb0a4c83ffcf27a02fe495ee/diff
/var/lib/docker/overlay2/1d05141f1e9f2fbc9bdfb87a681976ce7db6f1dae3c12a6d94c00197e07da019/diff
/var/lib/docker/overlay2/c5dee5abf71f4c0ecb219c68f19ca56f64b0400739319ed482994fb58c297f79/diff
/var/lib/docker/overlay2/ed5b345b9d1a113c250527826f268a24e556fc67314340a6082b9627bf99fc36/diff
/var/lib/docker/overlay2/2607ebf4a87c9a99966ad99c7c096d541114a3b4f75ab25e2bac3bf614f3485b/diff
mynginx:a
的 LowerDir
有 6 個檔案夾路徑,而且他的第一個檔案夾路徑竟然是 nginx
的 UpperDir
,是不是很有趣!而這些既然都是在 Host 上的檔案夾路徑,那讓我們進到 mynginx:a
的 UpperDir
看一下:
$ cd /var/lib/docker/overlay2/e6f2ed0e6d55115918467a74b4dfab3f3423c346cccdbe68763de8727061bf20/diff
$ ls
a.txt etc root run var
看到了嗎?在 mynginx:a
的 UpperDir
所記錄的檔案夾裡竟然有一個 a.txt,我們來 cat 他看看:
$ cat a.txt
a
果然真的是我們剛剛在 container 裡做的那個 a.txt,是不是太神奇了!?
到這裡,我們應該可以有個小小的結論,那就是 image 是由這些 layers 堆疊起來的。以我們的實驗來說,mynginx:a
是從 image nginx
啟動的 container 做出來的,透過觀察可以發現 mynginx:a
的 layers 跟 nginx
的 layers 一模一樣,可以說,那些一樣的 layers 就是 mynginx:a
與 nginx
共用的部分了。
以上的觀察不知道你們覺得好不好玩,我自己是覺得很好玩,親眼看到這些東西在哪裡,即使還是不知道為什麼,但總讓我覺得似乎又掌握 container 更多了一點,就是一種踏實感,好像不再神秘與虛無飄渺了!下一篇希望可以帶大家動手做更多的實驗與觀察,希望你們也能覺得有趣~
備註:
docker image list
或 docker images
來查看在 host 中的 images